#pragma once  
#include <cerrno>  
#include <cstring>  
#include <string>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include "Log.hpp"
#include "Error.hpp"

// 做一个规定：
// 输入: const &
// 输出: *
// 输入输出: &

static const int backlog = 20;

class Sock
{
public:
    Sock()
    {
    }
    ~Sock()
    {
    }
    // 创建套接字
    void Socket()
    {
        // int socket(int domain, int type, int protocol);
        // domain：域，是本地通信(AF_UNIX)还是跨网络通信(AF_INET)
        // type：类型
        // protocol：协议 默认为0，系统自动识别是tcp还是udp
        // 返回值：成功返回监听套接字，失败-1
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock < 0)
        {
            logMessage(Fatal, "socket filed...");
            exit(SOCKET_ERR);
        }
    }
    // 绑定端口，ip
    // 只有服务器才要绑定端口和ip，客户端不需要，客户端由os自动绑定
    void Bind(uint16_t port)
    {
        // int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
        // sockfd：要绑定哪个 套接字
        // addr：ip+port 一个特定结构
        // addrlen：对应的长度
        // 需要对sockaddr_in 进行强转
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        // 需要将主机序列转化为网络序列
        addr.sin_port = htons(port);
        // addr.sin_addr.s_addr = inet_addr(INADDR_ANY); // 在云服务器上不能自己去绑定特定的IP
        // inet_addr：将点分十进制的ip转化为4字节整数，并将它转化为网络序列
        // INADDR_ANY的类型并不是char*，是一个整数
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        socklen_t addrlen = sizeof(addr);
        int n = bind(_sock, (struct sockaddr *)&addr, addrlen);
        if (n < 0)
        {
            logMessage(Fatal, "bind failed..., [%d-%s]",errno,strerror(errno));
            exit(BIND_ERR);
        }
    }
    void Listen()
    {
        // int listen(int sockfd, int backlog);
        // sockfd：对哪个套接字进行监听
        // backlog：长连接的个数
        int n = listen(_sock, backlog);
        if (n < 0)
        {
            logMessage(Fatal, "listen failed...");
            exit(LISTEN_ERR);
        }
    }
    int Accept(std::string &ip, uint16_t &port)
    {
        // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        // 从监听套接字中获取新的连接
        // 并返回新连接对应的ip，port
        struct sockaddr_in addr;
        // 先清空一下
        memset(&addr, 0, sizeof(addr));
        socklen_t addrlen = sizeof(addr);
        int sock = accept(_sock, (struct sockaddr *)&addr, &addrlen);
        // 因为改为非阻塞了，这里的判断就不要了，在外面具体判断
        // if (sock < 0)
        // {
        //     logMessage(Warning, "accept failed...");
        //     exit(ACCEPT_ERR);
        // }
        ip = inet_ntoa(addr.sin_addr);
        port = ntohs(addr.sin_port);
        return sock;
    }
    int Connect(const std::string &ip, const uint16_t &port)
    {
        // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
        // 连接
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        addr.sin_port = htons(port);
        socklen_t addrlen = sizeof(addr);
        int n = connect(_sock, (struct sockaddr *)&addr, addrlen);
        if (n < 0)
        {
            logMessage(Fatal, "CONNECT_ERR");
            exit(CONNECT_ERR);
        }
        return n;
    }
    int Fd()
    {
        return _sock;
    }
    void Close()
    {
        close(_sock);
    }

private:
    int _sock;
};